Actuating a local output from a local input trigger

An important design criteria for my home automation is that nodes should be able to continue performing (basic) actions autonomously, even when they loose connectivity to the VSCP bus. Therefore I designed my Mespelare node so that it has inputs and outputs to manage a 6-way push-button face plate with LED indicators, and the pulse relays that need to be actuated with these buttons. I was planning on piping events sent by the node through its own Decision Matrix and trigger the pulse relays this way, but it turns out that VSCP nodes do not receive events they have sent out themselves, so they are not processed by the DM within the sending node. I could re-write the vscp_firmware so that sent events are echoed to the receive buffer, but this would make mean I am no longer using the standard vscp_firmware and would have to merge future updates into my code manually. So I decided to implement the response to key presses in application code.

Hooking into the code

We’ll hook into the part of the code that handles the processing of the inputs, namely the function ProcessInputs(). In this function the input buttons are debounced, and depending on their state (INP_PRESSED, INP_RELEASED, INP_HOLD, INP_HOLD_RELEASE) the corresponding events are sent onto the VSCP bus. We don’t just want the node to send an event upon a keypress, but also actuate an output, so we’ll add a line of code after each SendInformationEventData()-request to perform an action.

...
switch (INP_state[i]) {
	case INP_RELEASED:
		if (keyval == INP_PRESSED) {
			INP_state[i] = INP_PRESSED;
				if( controlbits & INP_CONTROLBIT_REDG ) {
					SendInformationEventData( i , VSCP_CLASS1_INFORMATION, VSCP_TYPE_INFORMATION_BUTTON, INP_PRESSED );
					ActuateUponLocalEvent( i );    //actuate local output corresponding to this button
                 }
...

The function ActuateUponLocalEvent( i ) (initially) looks like this:

void ActuateUponLocalEvent( uint8_t i ) {
	PulseOut( i ) ); //actuate local output corresponding to this button

In this function, we read the output where this input maps to from the register IN_OUT_MAPPINGx, and pulse this output. This effectively pulses output 1 whenever input 1 is triggered.

Configurable output mapping

We could make the input-to-output mapping static as in the above example (a keypress on input 1 always actuates output 1), but it makes sense to allow for configurable input-to-output mapping. Therefore we’ll introduce a few new registers.

In the Module Description File:

<!-- Input-to-output mapping -->
    <reg page="0" offset="121">
		<name lang="en">Input-to-output mapping IN1 (IN_OUT_MAPPING)</name>
		<description lang="en">
			This register holds a value which maps an input to an output, so that when the input is activated, the output will actuate.
		</description>
		<access>rw</access>
	</reg>		

These correspond to these registers in the firmware (VSCP_node_defines.h):

#define IN_OUT_MAPPING1		(256* 0 + 121)
#define IN_OUT_MAPPING2		(256* 0 + 122)
#define IN_OUT_MAPPING3		(256* 0 + 123)
#define IN_OUT_MAPPING4		(256* 0 + 124)
#define IN_OUT_MAPPING5		(256* 0 + 125)
#define IN_OUT_MAPPING6		(256* 0 + 126)
#define IN_OUT_MAPPING7		(256* 0 + 127)

The registers Input-to-output mapping INx (IN_OUT_MAPPING) define for each input which corresponding output will be actuated. If we for example load the register Input-to-output mapping IN3 (IN_OUT_MAPPING) with a value of 5, whenever button 3 is pressed, output 5 will be actuated.

We’ll re-write our function ActuateUponLocalEvent() like this:

void ActuateUponLocalEvent( uint8_t i ) {
    PulseOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ) ); //actuate local output corresponding to this button

Now whenever an input is triggered, the corresponding IN_OUT_MAPPING register will be read and the output defined in that register will be pulsed.

Configurable output mode

Up to now the response to an input trigger was a fixed action, PulseOut(). We’ll want to make this configurable as well, so we’ll introduce another register.

In the Module Description File:

<reg page="0" offset="120">
	<name lang="en">Input-to-output mapping action (IN_OUT_MAPPING_ACTION)</name>
	<description lang="en">
		This register determines which action will be performed when mapping an input to an output. The value of these actions correspond to the action code as defined in the DM.
	</description>
	<access>rw</access>
</reg>		

The corresponding register in the firmware (VSCP_node_defines.h):

#define IN_OUT_MAPPING_ACTION	(256* 0 + 120)

We’ll re-write our function ActuateUponLocalEvent() like this:

void ActuateUponLocalEvent( uint8_t i ) {
	switch (readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING_ACTION ) ) {
		case ACTION_ON:
			SetOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ), 1);  //switch output off
			break;
		case ACTION_OFF:
			SetOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ), 0);  //switch output on
			break;
		case ACTION_PULSE:
			PulseOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ) ); //Actuating a local output from a local input trigger actuate local output corresponding to this button
			break;
		case ACTION_TOGGLE:
			if ( ReadOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ) ) ) {  // if output is on
				SetOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ), OUTPUT_OFF);
			} else {
				SetOut( readEEPROM( VSCP_EEPROM_END + IN_OUT_MAPPING1 + i ), OUTPUT_ON);
			}
			break;
		default:
			break;
	}
}

Now every time ActuateUponLocalEvent() is called, the register IN_OUT_MAPPING_ACTION to check which action to perform, and then the correct function for that action will be called.

Do note that using a simple register for all input-to-output mappings means that we can only set the action to perform for all registers, not for individual registers.

Resources

Comments